package com.liusy.mapcache;


import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 本缓存采用的是 LRU 策略实现。默认缓存长度为1024，缓存刷新时间为15分钟，缓存过滤键值为缓存所有。
 * @author xxwinnie
 *
 */
public class MapCacheImpl implements IMapCache {
    
    private Logger log = LoggerFactory.getLogger(MapCacheImpl.class);
    
    /**
     * 需要进行缓存处理的Key（可使用正则表达式）
     */
    private String[] cacheKeys = new String[]{".*"};
    
    public void setCacheKeys(String[] cacheKeys) {
        this.cacheKeys = cacheKeys;
    }
    
    /**
     * 缓存大小
     */
    private int cacheSize = 1024;
    
    public void setCacheSize(int cacheSize) {
        this.cacheSize = cacheSize;
    }
    
    /**
     * 默认时间内的有效缓存数据
     */
    private long defaultRefreshInterval = REFRESH_INTERVAL_15MIN;
    
    public void setDefaultRefreshInterval(long refreshInterval) {
        this.defaultRefreshInterval = refreshInterval;
    }
    
    private Map<String, TimestampValue<?>> cacheData = new HashMap<String, TimestampValue<?>>();
    
    @Override
    public <T> T getData(String dataKey, Class<T> dataType) {
        return this.getData(dataKey, dataType, defaultRefreshInterval);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public <T> T getData(String dataKey, Class<T> dataType, long refreshInterval) {
        T result = null;
        if (checkDataTimestampValid(dataKey, refreshInterval)) {
            try {
                TimestampValue<T> dataTmp = (TimestampValue<T>) cacheData.get(dataKey);
                //dataTmp.setMyTimestamp(new Date().getTime());
                return dataTmp.getMyData();
            } catch (Exception e) {
                log.warn("get cached data ERROR: " + dataKey + " - refreshInterval: " + refreshInterval);
                e.printStackTrace();
            }
        }
        return result;
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public synchronized <T> T updateData(String dataKey, T data) {
        if (data != null) {
            if (!this.checkKeyNeedCache(dataKey)) {
                log.info("bypass this key: " + dataKey);
                return data;
            }
            long nowDate = new Date().getTime();
            if (cacheData.containsKey(dataKey)) {
                try {
                    TimestampValue<T> dataTmp = (TimestampValue<T>) cacheData.get(dataKey);
                    dataTmp.setMyData(data);
                    dataTmp.setMyTimestamp(nowDate);
                    log.info("updated a data: " + dataKey + " - " + data);
                } catch (Exception e) {
                    log.error("update data ERROR: " + dataKey + " - " + data);
                    e.printStackTrace();
                }
            } else {
                try {
                    if (cacheData.size()+1 > cacheSize) {
                        String oldestKey = this.getOldestKey();
                        cacheData.remove(oldestKey);
                        log.info("cache over size, remove the oldest Data: " + oldestKey);
                    }
                    cacheData.put(dataKey, new TimestampValue<T>(data, nowDate));
                    log.info("putted a data: " + dataKey + " - " + data);
                } catch (Exception e) {
                    log.error("insert new data ERROR: " + dataKey + " - " + data);
                    e.printStackTrace();
                }
            }
        }
        log.debug("the map size after updateData(...): " + this.cacheData.size());
        return data;
    }
    
    /**
     * 获取Map中的时间标签最老的Key。
     * @return
     */
    private String getOldestKey() {
        String oldestKey = "";
        long oldestTime = Long.MAX_VALUE;
        for (String keyTmp: cacheData.keySet()) {
            TimestampValue<?> tvTmp = cacheData.get(keyTmp);
            if (tvTmp.getMyTimestamp() < oldestTime) {
                oldestTime = tvTmp.getMyTimestamp();
                oldestKey = keyTmp;
            }
        }
        return oldestKey;
    }
    
    @Override
    public int getCurrentCount() {
        int mapSize = this.cacheData.size();
        log.info("getCurrentCount(): " + mapSize);
        return mapSize;
    }
    
    @Override
    public String[] getCurrentKeys() {
        log.info("getCurrentKeys(): " + this.cacheKeys);
        return this.cacheKeys;
    }
    
    /**
     * 判断指定的Key是否在缓存目标范围内（正则表达式）
     * @param myKey
     * @return
     */
    private boolean checkKeyNeedCache(String myKey) {
        if (this.cacheKeys != null  && this.cacheKeys.length > 0) {
            for (String key: this.cacheKeys) {
                if (myKey.matches(key)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    /**
     * 检查缓存数据是否有效，
     * @param myKey  用来查数的key
     * @param refreshInterval   设置的刷新时间间隔
     * @return  如果缓存数据有效，则返回true，如果无效，则为false
     */
    private boolean checkDataTimestampValid(String myKey, long refreshInterval) {
        //TODO 此处可使用开源的缓存组件，减少数据库访问频度。有待研究。
        try {
            TimestampValue<?> myTv = cacheData.get(myKey);
            if (myTv == null) {
                log.warn("there's no cache: " + myKey);
                return false;
            } else if (myTv.getMyTimestamp() + refreshInterval < new Date().getTime()) {
                log.warn("this cache is timeout: " + myKey);
                return false;
            } else {
                return true;
            }
        } catch(Exception e) {
            log.warn("check data valid ERROR: " + myKey + " refreshInterval: " + refreshInterval);
            e.printStackTrace();
            return false;
        }
    }
}

